#!/usr/bin/env python
# -*- coding: utf-8 -*
# author: SAI


import os,sys,time,platform

def get_macosx_shellcode(filepath):
    cmdline='otool -tX '+filepath
    #print cmdline
    rl=os.popen(cmdline).readlines()
    shellcode=''
    for i in rl:
        cl=i.split(' ')
        cl=cl[1:-1]
        for code in cl:
            s='\\x'+code
            shellcode+=s
    print 'char shellcode[]="%s";' % shellcode
    print 'shellcode size:%d' % (len(shellcode)/4)
    if shellcode.find('00')!=-1:
        print 'notice:0x00 is in shellcode!'


def get_linux_shellcode(filepath):
    cmdline='objdump -d '+filepath
    #print cmdline
    rl=os.popen(cmdline).readlines()
    shellcode=''
    for i in rl:
        cl=i.split('\t')
        if len(cl)==3:
            sl=cl[1].split(' ')
            for code in sl:
                if code!='':
                    s='\\x'+code
                    shellcode+=s

    print 'char shellcode[]="%s";' % shellcode
    print 'shellcode size:%d' % (len(shellcode)/4)
    if shellcode.find('00')!=-1:
        print 'notice:0x00 is in shellcode!'

def analyze_aslr(target_name,target_list,times):
    print '='*30
    #for i in target_list:
    #    print '%x' % i
    max=0xffff
    for i in xrange(8):
        d=target_list[0]|max
        goodbits=1
        vmax=target_list[0]
        for j in target_list[1:]:
            if j>vmax:
                vmax=j
            j=j|max
            if j!=d:
                goodbits=0
                break
        if goodbits==1:
            break
        max=(max<<4)+0xf
    vmax-=vmax%((max+1)>>4)
    vmax|=(max>>4)
    highbits=d-max
    min=max
    for i in xrange(8):
        d=target_list[0] & min 
        goodbits=1
        for j in target_list[1:]:
            j=j&min
            if j!=d:
                goodbits=0
                break
        if goodbits==1:
            break
        min>>=4
    print '%s range:0x%x-0x%x' % (target_name,highbits|(min+1),vmax)
    print '%d difference in %d tests' % (len(set(target_list)),len(target_list))
    return 

def test_aslr(times):
    cmdline='./sc -ta '
    heap=[]
    stack=[]
    lib=[]
    main=[]
    for i in xrange(times):
        r=os.popen(cmdline).readlines()
        for j in r:
            j=j[:-1]
            j=j.split(':')
            if j[0]=='heap':
                heap.append(int(j[1],16))
            elif j[0]=='stack':
                stack.append(int(j[1],16))
            elif j[0]=='lib':
                lib.append(int(j[1],16))
            elif j[0]=='main':
                main.append(int(j[1],16))
    analyze_aslr('heap',heap,times)
    analyze_aslr('stack',stack,times)
    analyze_aslr('lib',lib,times)
    analyze_aslr('main',main,times)

def usage():
   myname=sys.argv[0]
   print '%s -p bin (print shellcode)' % sys.argv[0]
   print '%s -aslr times (test aslr)' % myname
   print '%s -h (help)' % myname

if __name__=='__main__':
    if len(sys.argv)<3:
        usage()
        sys.exit(0)
    
    option=sys.argv[1]
    if option=='-aslr':
        times=sys.argv[2]
        if not times.isdigit():
            print 'times invalid'
            sys.exit(0)
        times=int(times)
        if times<10:
            print 'times should >=10'
            sys.exit(0)
        test_aslr(times)


    elif option=='-p':
        filepath=sys.argv[2]
        osname=platform.system()
        if osname=='Windows':
            print 'sorry,not support windows now'
        elif osname=='Linux':
            get_linux_shellcode(filepath)
        elif osname=='Darwin':
            get_macosx_shellcode(filepath)
        else:
            print 'not support unknown os'
    else:
        usage()

